package com.qzxy.premiumhotelsystem.controller.guest;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qzxy.premiumhotelsystem.common.annotation.LogExecutionTime;
import com.qzxy.premiumhotelsystem.common.constant.CaptchaConstant;
import com.qzxy.premiumhotelsystem.common.constant.UserRoleConstant;
import com.qzxy.premiumhotelsystem.common.enums.LogLevel;
import com.qzxy.premiumhotelsystem.common.exception.SqlException;
import com.qzxy.premiumhotelsystem.domain.dto.GuestAndVipDTO;
import com.qzxy.premiumhotelsystem.domain.dto.GuestDTO;
import com.qzxy.premiumhotelsystem.domain.entity.Guests;
import com.qzxy.premiumhotelsystem.domain.entity.Orders;
import com.qzxy.premiumhotelsystem.domain.vo.GuestInfoVo;
import com.qzxy.premiumhotelsystem.domain.vo.LoginVo;
import com.qzxy.premiumhotelsystem.domain.vo.UpdatePasswordVo;
import com.qzxy.premiumhotelsystem.common.result.Result;
import com.qzxy.premiumhotelsystem.service.GuestsService;
import com.qzxy.premiumhotelsystem.service.LogsService;
import com.qzxy.premiumhotelsystem.service.OrdersService;
import com.qzxy.premiumhotelsystem.utils.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.apache.ibatis.annotations.Delete;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static cn.dev33.satoken.SaManager.log;
import static com.qzxy.premiumhotelsystem.common.result.ResultCodeEnum.*;

/**
 * @Author: iQNRen
 * @Description: 顾客接口
 * @Date: 2024/9/17 22:08 周二
 * @Version: 1.0
 */
@Tag(name = "用户接口")
@RestController
//@SaCheckRole(value = {UserRoleConstant.GUEST, UserRoleConstant.EMPLOYEE, UserRoleConstant.ADMIN}, mode = SaMode.OR)
@RequestMapping("/guests")
public class GuestsController {
    @Resource
    private GuestsService guestsService;
    @Resource
    private AliOSSUtils aliOSSUtils;
    @Resource
    private Bcrypt bcrypt;
    @Resource
    private OrdersService ordersService;

    @Operation(summary = "添加用户", description = "管理员权限")
    @SaCheckRole(type = UserRoleConstant.ADMIN)
    @PostMapping("/create")
    @LogExecutionTime(message = "添加用户")
    public Result createEmployees(@RequestBody @Valid Guests guests) {
        guests.setPassword(bcrypt.encode(guests.getPassword()));
        try {
            if (guestsService.save(guests)) {
//                logsService.create("创建用户" + guests.getTelephone());
                return Result.ok("创建用户成功");
            } else {
//                logsService.create("创建用户" + guests.getTelephone() + "失败");
                return Result.ok("创建用户失败");
            }
        } catch (SqlException e) {
            throw new SqlException("插入失败，违反唯一约束或主键冲突。");
        }

    }

    @Operation(summary = "删除用户", description = "管理员权限")
    @SaCheckRole(type = UserRoleConstant.ADMIN)
    @DeleteMapping("/delete/{telephone}")
    @LogExecutionTime(message = "删除用户",level = LogLevel.ERROR)
    public Result deleteEmployees(@PathVariable String telephone) {
        boolean b = guestsService.deleteByTelephoneBoolean(telephone);
        if (b != false) {
//            logsService.create("删除用户" + telephone);
            return Result.ok("用户删除成功");
        } else {
//            logsService.create("删除用户" + telephone + "失败");
            return Result.ok("用户删除失败");
        }
    }

    @Operation(summary = "更新用户全部信息", description = "管理员权限")
    @SaCheckRole(type = UserRoleConstant.ADMIN)
    @PutMapping("/updateall")
    @LogExecutionTime(message = "更新用户全部信息",level = LogLevel.WARN)
    public Result updateAllEmployees(@RequestBody @Valid Guests guests) {

        try {
            if (guestsService.updateAllInformation(guests) != false) {
//                logsService.create("更新用户全部信息" + guests.getTelephone());
                return Result.ok("更新用户全部信息成功");
            } else {
//                logsService.create("更新用户全部信息" + guests.getTelephone() + "失败");
                return Result.ok("更新用户全部信息失败");
            }
        } catch (
                Exception e) {
            throw new RuntimeException("更新失败，注意tel准确性", e);
        }

    }


    @Operation(summary = "顾客查询(电话)")
    @GetMapping("/tel/{telephone}")
    public Result searchTel(@PathVariable @NotBlank String telephone) {
        return Result.ok(guestsService.getByTelephone(telephone));
    }

    @Operation(summary = "顾客分页查询")
    @GetMapping("/page")
    public Result Page(@RequestParam @Valid Integer pageSize, @RequestParam @Valid Integer limit) {
        return Result.ok(guestsService.allGuestsForPage(pageSize, limit));
    }


    /**
     * 更新顾客基本信息
     *
     * @param guestInfoVo
     * @return
     */
    @Operation(summary = "顾客更新基本信息")
    @PatchMapping("/update-info/")
    @LogExecutionTime(message = "顾客更新基本信息")
    public Result updateInfo(@RequestBody @Valid GuestInfoVo guestInfoVo) {
        return Result.ok(guestsService.updateInformation(guestInfoVo));
    }


    @PostMapping("/upload")
    @Operation(summary = "更新头像")
    @LogExecutionTime(message = "更新头像")
    public Result uploadByAliOOS(MultipartFile file) throws Exception {
        String url = aliOSSUtils.upload(file);
//        String fileName = url.substring(url.lastIndexOf('/') + 1);
//        log.info("文件上传完成, 文件名:{},文件访问的url: {}", fileName, url);
        guestsService.uploadAvatar(url, StpUtil.getLoginId().toString());
//        logsService.create("文件上传完成");
        // 将用户头像地址写入数据库 并返回给前端
        return Result.ok(url);
    }

    @GetMapping("/download")
    @Operation(summary = "用户头像")
    @SaIgnore
    public Result downloadAvatar(String telephone) throws IOException {
        String imgUrl = guestsService.getByTelephone(telephone).getImgUrl();
//        String download = aliOSSUtils.download(imgUrl);
        return Result.ok(imgUrl);
    }

    /**
     * 用户注册
     *
     * @param guests
     * @param Vcode
     * @return
     * @throws Exception
     */
    @Operation(summary = "注册")
//    @SaIgnore
    @PostMapping("/verification/register/{Vcode}")
    public Result verification(@RequestBody @Valid Guests guests, @PathVariable(value = "Vcode") @Valid String Vcode) throws Exception {
        String key = CaptchaConstant.REGISTER_V_CODE.concat("_" + guests.getEmail());
        if (!KeysUtil.validateCaptcha(key, Vcode)) {
            return Result.error("验证码错误 请重新输入");
        }

        if (!Objects.isNull(guestsService.getByTelephone(guests.getTelephone()))) {
            return Result.error("该手机号已存在");
        } else {
            guests.setPassword(bcrypt.encode(guests.getPassword()));
            try {
                if (guestsService.save(guests) != false) {
                    return Result.ok("注册成功");
                }
                return Result.error("注册失败");
            } catch (SqlException e) {
                throw new SqlException("插入失败，违反唯一约束或主键冲突。");
            }
        }

    }

    /**
     * 用户登录
     *
     * @param loginVo
     * @param Vcode
     * @return
     */
    @Operation(summary = "登录")
//    @SaIgnore
    @PostMapping("/verification/login/{Vcode}")
    @Transactional
    @LogExecutionTime(message = "登录")
    public Result Login(@RequestBody @Valid LoginVo loginVo, @PathVariable(value = "Vcode") @NotBlank String Vcode, HttpServletRequest request) {
        String key = CaptchaConstant.LOGIN_V_CODE.concat("_" + IpUtil.getClientIp(request));

        if (!KeysUtil.validateCaptcha(key, Vcode)) {
            return Result.build("验证码错误 请重新输入", CAPTCHA_INVALID);
        }
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println("当前时间戳为：" + time);

        String login = guestsService.login(loginVo);
        if (!login.isBlank()) {
            if (bcrypt.matches(loginVo.getPassword(), login)) {
                Guests guests = guestsService.getByTelephone(loginVo.getTelephone());
                //切换为guest用户，进行登录
//                StpUtil.setStpLogic(guestStpLogic);
                StpUtil.login(loginVo.getTelephone(), new SaLoginModel()
                        .setExtra("ids", guests.getGuestId())
                        .setExtra("email", guests.getEmail())
                        .setExtra("nickname", guests.getNickname())
                        .setExtra("role", UserRoleConstant.GUEST));
                guestsService.updateLoginStatus(IpUtil.getClientIp(request), time, loginVo.getTelephone());
                return Result.ok(StpUtil.getTokenInfo().tokenValue);
            } else {
                return Result.build("密码错误", PASSWORD_ERROR);
            }
        } else {
            return Result.build("用户不存在", USERNAME_ERROR);
        }
    }


    /**
     * 获取个人信息
     * @return
     */
    @Operation(summary = "个人信息")
//    @SaIgnore
    @PostMapping("/me")
    @LogExecutionTime(message = "个人信息")
    public Result Me() {
        GuestAndVipDTO vipType = guestsService.getVipType(StpUtil.getLoginId().toString());
        System.out.println("vipType:"+vipType);
        if (vipType!=null) {
            return Result.ok(vipType);
        } else {
            return Result.error("请先登录");
        }
    }



    @Operation(summary = "密码更新")
    @PatchMapping("/update-password")
    @LogExecutionTime(message = "密码更新",level = LogLevel.DEBUG)
    public Result UpdatePassword(@RequestBody @Valid UpdatePasswordVo updatePasswordVo) {
        if (!Objects.equals(updatePasswordVo.getPassword(), updatePasswordVo.getRepassword())) {
            return Result.error("两次密码不一致 请重新输入");
        }
        String key = CaptchaConstant.UPDATE_V_CODE.concat("_" + updatePasswordVo.getEmail());
        System.out.println("CaptchaStorecode::" + KeysUtil.getCaptcha(key));
        if (!KeysUtil.validateCaptcha(key, updatePasswordVo.getVcode())) {
            return Result.error("验证码错误 请重新输入");
        }

        if (Objects.isNull(guestsService.getByEmail(updatePasswordVo.getEmail()))) {
            return Result.error("该用户不存在");
        } else {
            Guests guests = guestsService.getByEmail(updatePasswordVo.getEmail());
            guests.setPassword(bcrypt.encode(updatePasswordVo.getPassword()));
            try {
                if (guestsService.updateById(guests) != false) {
                    return Result.ok("更新密码成功");
                }
                return Result.error("更新密码失败");
            } catch (SqlException e) {
                throw new SqlException("插入失败，违反唯一约束或主键冲突。");
            }
        }
    }

    /**
     * 退出登录
     *
     * @return
     */
    @SaCheckLogin
    @Operation(summary = "退出登录")
    @Transactional
    @PostMapping("/logout")
    @LogExecutionTime(message = "退出登录")
    public Result logout() {
//        logsService.create("退出登录");
        StpUtil.logout();
        return Result.ok("退出登录成功");
    }


    /**
     * 重置密码
     *
     * @param email
     * @param Vcode
     * @return
     */
    @Operation(summary = "密码重置")
    @LogExecutionTime(message = "密码重置",level = LogLevel.DEBUG)
//    @SaIgnore
    @PatchMapping("/forger-password/verification/{email}/{Vcode}")
    public Result forgerPassword(@PathVariable @NotBlank String email, @PathVariable(value = "Vcode") @NotBlank String Vcode) {
        String key = CaptchaConstant.FORGET_V_CODE.concat("_" + email);
        System.out.println("key：" + key + "CaptchaStorecode::" + KeysUtil.getCaptcha(key));
        if (!KeysUtil.validateCaptcha(key, Vcode)) {
            return Result.error("验证码错误 请重新输入");
        }
        if (Objects.isNull(guestsService.getByEmail(email))) {
            return Result.error("该用户不存在");
        } else {
            Guests guests = guestsService.getByEmail(email);
            guests.setPassword(bcrypt.encode("123456"));
            try {
                if (guestsService.updateById(guests) != false) {
                    return Result.ok("重置密码成功");
                }
                return Result.error("重置密码失败");
            } catch (SqlException e) {
                throw new SqlException("插入失败，违反唯一约束或主键冲突。");
            }
        }
    }


    /**
     * 查看登录会话是否登录
     *
     * @return
     */
    @Operation(summary = "顾客登录会话", description = "查看登录会话是否登录")
//    @SaCheckLogin(type = UserRoleConstant.GUEST)
    @GetMapping("/is-login")
    public Result isLogin() {
        // 查看当前会话是否登录
        boolean isLogin = StpUtil.isLogin();
        String token = isLogin ? (String) StpUtil.getTokenInfo().tokenValue : null;
        return Result.build(token, isLogin ? 200 : 403, isLogin ? "用户已经登录" : "用户未登录无权操作");
    }

    /**
     * 检查是否登录
     *
     * @return
     */
    @Operation(summary = "顾客是否登录", description = "顾客是否登录")
//    @SaCheckLogin(type = UserRoleConstant.GUEST)
    @GetMapping("/check-login")
    public Result checkLogin() {
        // 未登录将直接报错
        StpUtil.checkLogin();
        return Result.ok("顾客已经登录");
    }











    /**
     * 获取全部用户状态（便于管理员与用户管理）
     * @return
     */
    @Operation(summary = "所有顾客入住信息（员工）", description = "所有顾客入住信息（员工）")
    @GetMapping("/manage/guestsForOrderStatusAll")
    public Result guestsForOrderStatusAll(){
        return Result.ok(guestsService.guestsForOrderStatusAll());
    }
    /**
     * 获取当前要预定与入住的用户状态（便于管理员与用户管理）
     * @return
     */
    @Operation(summary = "要入住顾客入住信息（员工）", description = "要入住顾客入住信息（员工）")
    @GetMapping("/manage/guestsForOrderStatus")
    public Result guestsForOrderStatus(){
        return Result.ok(guestsService.guestsForOrderStatus());
    }
    /**
     * 获取历史用户状态（便于管理员与用户管理）
     * @return
     */
    @Operation(summary = "历史顾客入住信息（员工）", description = "历史顾客入住信息（员工）")
    @GetMapping("/manage/guestsForOrderStatusHistory")
    public Result guestsForOrderStatusHistory(){
        return Result.ok(guestsService.guestsForOrderStatusHistory());
    }

    @Operation(summary = "修改顾客入住状态（员工）", description = "修改顾客入住状态（员工）")
    @LogExecutionTime(message = "修改顾客入住状态",level = LogLevel.WARN)
    @PatchMapping ("/manage/updateUserState/{orderId}/{state}")
    public Result updateUserState(@PathVariable String orderId, @PathVariable String state ){
        Orders one = ordersService.getById(orderId);
        System.out.println(one);
        one.setStatus(state);
        ordersService.updateById(one);
        return Result.ok("成功");
    }

}

